%%%-------------------------------------------------------------------
%%% File    : gate_server.erl
%%% Author  :  <>
%%% Description : 
%%%
%%% Created : 16 Mar 2011 by  <>
%%%-------------------------------------------------------------------
-module(gate_server).

-behaviour(gen_server).

-export([start/1, start_link/1]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	terminate/2, code_change/3]).

-export([acceptor/2]).

-define(SERVER, ?MODULE).

-define(PORT, 8888).

-define(Size, 20).

-define(Max, 5000).

-record(state, {sup,
	       lsock,
	       port,
	       pool,
	       flash}).

%%--------
start(Sup) ->
    gen_server:start({local, ?SERVER}, ?MODULE, [Sup, ?PORT], []).
start_link(Sup) ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [Sup, ?PORT], []).
%%---------
init([Sup, Port]) ->
    process_flag(trap_exit, true),
     Opts = [binary,
	   {reuseaddr, true},
	   {packet, 2},
	   {backlog, 100},
	   {active, false},
	   {nodelay, true}],
    {Tag, Lsock} = gen_tcp:listen(Port, Opts),
    case Tag of
	ok ->
	    Func = fun(_, List) -> 
			   Pid = spawn_link(?MODULE, acceptor, [self(), Lsock]),
			   [Pid|List] end,
	    Pool = start_pool(Func, ?Size),
	    case flash_server:start_link(self()) of
		{ok, Flash} ->
		     {ok, #state{lsock=Lsock,
				 port=Port,
				 pool=Pool,
				 sup=Sup,
				 flash=Flash}};
		Err ->
		    {stop, {error, Err}}
             end;
	error ->
	    {stop, {error,Lsock}}
     end.

handle_call(_Rq, _From, S) ->
    {reply, ok, S}.

handle_cast({acceptor, Pid}, S=#state{lsock=Lsock,
				      pool=Pool}) ->
    Npid = spawn_link(?MODULE,acceptor, [self(), Lsock]),
    {noreply, S#state{pool=[Npid|lists:delete(Pid, Pool)]}};

handle_cast(redirect, S) ->
    redirect(S);

handle_cast({do_exit, Pid}, S=#state{sup=Sup}) ->
    case Pid =:= Sup of
	true ->
	    {stop, normal, S};
	false ->
	    {noreply, S}
    end;

handle_cast(_Msg, S) ->
    {noreply, S}.

handle_info({'EXIT', _, normal}, S) ->
    {noreply, S};

handle_info({'EXIT', Pid, shutdown}, S=#state{sup=Sup}) ->
    case Pid =:= Sup of
	true ->
	    {stop, shutdown, S};
	false ->
	    {noreply, S}
    end;

handle_info({'EXIT', Pid, _Reason}, S=#state{sup=Sup,		  
					    flash=Flash}) ->
    case Pid of
	Sup ->
	    redirect(S);
	Flash ->
	    restart_flash(S);
	_Other ->
	    restart_pool(Pid, S)
    end;
    
handle_info(_Info, S) ->
    {noreply, S}.

terminate(_Reason, _S) ->
    ok.

code_change(_Oldvsn, S, _Extra) ->
    {ok, S}.

%%--------internal

start_pool(Fun, Size) ->
    lists:foldl(Fun, [], lists:seq(1, Size)).

acceptor(Parent, Lsock) ->
    {Tag, Sock} = gen_tcp:accept(Lsock),
    case Tag of
	ok ->
	    gen_server:cast(Parent, {acceptor, self()}),
	    loop(Sock),
	    ok;
	error ->
	    gen_server:cast(Parent, {acceptor, self()})
    end.


redirect(S) ->
    case global:whereis_name(gate_sup) of
	Pid when is_pid(Pid) ->
	    link(Pid),
	    gen_server:cast(Pid, {redirect, node(), self()}),
	    {noreply, S#state{sup=Pid}};
	undefined ->
	    gen_server:cast(self(), redirect),
	    {noreply, S}
    end.

restart_flash(S) ->
    Res = flash_server:start_link(self()),
    case Res of
	{ok, Pid} ->
	    {noreply, S#state{flash=Pid}};
	Err ->
	    {stop, Err, S}
    end.

restart_pool(Pid, S=#state{lsock=Lsock,
			  pool=Pool}) ->
    case lists:member(Pid, Pool) of
	true ->
	    Npid = spawn_link(?MODULE, acceptor, [self(), Lsock]),
	    {noreply, S#state{pool=[Npid|lists:delete(Pid, Pool)]}};
	false ->
	    {noreply, S}
    end.

loop(Sock) ->
     Data = <<127:8, 115:8, 63:8, 78:8, 8080, 16>>,
     gen_tcp:send(Sock, Data).
   %% case find_server(?Max) of
%%	{{N1, N2, N3, N4}, Port} ->
%%	   %% Data = <<N1:8, N2:8, N3:8, N4:8, Port, 16>>,
%%	    Data = <<127:8, 115:8, 63:8, 78:8, 8080, 16>>,
%%	    gen_tcp:send(Sock, Data), 
%%	    ok;
%%	_Any ->
%%	    ok
 %%  end.

find_server(Max) ->
    case pg2:get_closest_pid(net) of
	Pid when is_pid(Pid) ->
	    {Host, Port, Count} = gen_server:call(Pid, 'WHERE AND COUNT'),
	    if
		Count < Max ->
		    io:format("~s:~w: ~w players~n", [Host, Port, Count]),
		    {Host, Port};
		true ->
		    io:format("~s:~w is full...~n", [Host, Port]),
		    find_server(Max)
            end;
	Any ->
	    Any
    end.
